﻿//-----------------------------------------------------------------------
// <copyright file="UpdateManager.cs" author="Artak Mkrtchyan">
//     Creative Commons - GNU General Public License (http://creativecommons.org/licenses/GPL/2.0/
// </copyright>
// <author>Artak Mkrtchyan</author>
// <email>mkArtak@gmail.com</email>
// <date>08/12/2012</date>
//-----------------------------------------------------------------------
using AppUpdate.Common;
using AppUpdateInstaller.Model;
using ConsoleHoster.Common.Utilities;
using ConsoleHoster.Common.Utilities.IO;
using ConsoleHoster.Model.Configuration;
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.ServiceModel;
using System.Threading.Tasks;

namespace ConsoleHoster.Model
{
	public sealed class UpdateManager : IUpdateManager
	{
		private const string UpdateInstallerName = "AppUpdateInstaller.exe";

		private readonly IConfigurationProvider settingsProvider;
		private readonly string applicationName;
		private readonly IFileSystemContentProvider directoryContentProvider;
		private readonly ILogger logger;
		private readonly IProcessManager processManager;

		public UpdateManager(string argApplicationName, IProcessManager argProcessManager, ILogger argLogger)
			: this(argApplicationName, new DefaultConfigurationProvider(key => ConfigurationManager.AppSettings[key], argLogger), argProcessManager, new FileSystemContentProvider(), argLogger)
		{
		}

		internal UpdateManager(string argApplicationName, IConfigurationProvider argSettingsProvider, IProcessManager argProcessManager, IFileSystemContentProvider argDirectoryContentProvider, ILogger argLogger)
		{
			if (String.IsNullOrWhiteSpace(argApplicationName))
			{
				throw new ArgumentException("Invalid or missing application name");
			}

			ValidationManager.RequireArgumentNotNull(argSettingsProvider, "argSettingsProvider");
			ValidationManager.RequireArgumentNotNull(argDirectoryContentProvider, "argDirectoryContentProvider");
			ValidationManager.RequireArgumentNotNull(argProcessManager, "argProcessManager");

			this.applicationName = argApplicationName;
			this.settingsProvider = argSettingsProvider;
			this.processManager = argProcessManager;
			this.directoryContentProvider = argDirectoryContentProvider;
			this.logger = new ComponentLogger("UpdateManager", argLogger);
		}

		public InstallerPackageDetails CheckUpdateAvailability(VersionInfo argCurrentVersion)
		{
			this.logger.LogMessage("Checking for update availability for version: {0}", argCurrentVersion.Version);
			InstallerPackageDetails tmpResult = null;

			ChannelFactory<IUpdateService> tmpChannelFactory = null;
			try
			{
				this.logger.LogMessage("Establishing connection with the update service...");

				tmpChannelFactory = new ChannelFactory<IUpdateService>(this.settingsProvider.UpdateServiceEndpointConfigurationName);
				IUpdateService tmpClient = tmpChannelFactory.CreateChannel();

				this.logger.LogMessage("Connection established. Getting latest version...");
				InstallerPackageDetails tmpPackageDetails = tmpClient.GetLatestVersion(this.applicationName);
				this.logger.LogMessage("Latest version retrieved is: {0}", tmpPackageDetails.Version.Version);
				if (tmpPackageDetails.Version.Version.CompareTo(argCurrentVersion.Version) > 0)
				{
					this.logger.LogMessage("Update available.");
					tmpResult = tmpPackageDetails;
				}
				else
				{
					this.logger.LogMessage("No updates available");
				}
			}
			finally
			{
				if (tmpChannelFactory != null)
				{
					tmpChannelFactory.Close();
				}
			}

			return tmpResult;
		}

		public async Task<string> DownloadInstallerPackageAsync(InstallerPackageDetails argInfo)
		{
			string tmpFilename = this.GetTempFilename();
			string tmpPath = Path.Combine(this.GetTempFolderForUpdate(argInfo.Version.Version), tmpFilename);
			using (WebClient tmpClient = new WebClient())
			{
				await tmpClient.DownloadFileTaskAsync(argInfo.DownloadUri, tmpPath);
			}
			return tmpPath;
		}

		public void BeginUpdate(string argInstallerFileLocation)
		{
			using (this.processManager.StartProcess(UpdateInstallerName, StringUtilities.Format("{0}, {1}, {2}", InstallerMode.ExtractArchive.ToString(), argInstallerFileLocation, Directory.GetCurrentDirectory())))
			{
			}
		}

		private string GetTempFolderForUpdate(string argVersion)
		{
			string tmpDirectory = this.settingsProvider.TempFolderName;
			if (!this.directoryContentProvider.DirectoryExists(tmpDirectory))
			{
				this.directoryContentProvider.CreateDirectory(tmpDirectory);
			}
			string tmpResult = Path.Combine(tmpDirectory, argVersion);
			if (!this.directoryContentProvider.DirectoryExists(tmpResult))
			{
				this.directoryContentProvider.CreateDirectory(tmpResult);
			}
			return tmpResult;
		}

		private string GetTempFilename()
		{
			return String.Format("{0}.zip", this.applicationName);
		}
	}
}
